package com.squid.base.utils;

import com.squid.base.common.error.ExceptionFactory;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redisson redis 工具
 *
 * @author jinhang1
 * @date 2024/07/25
 */
@Slf4j
@Component
public class RedissonRedisUtils {
    @Resource
    RedissonClient redissonClient;

    /**
     * 设置string的值并设置过期时间
     *
     * @param key         关键
     * @param value       价值
     * @param expiredTime 过期时间
     */
    public void setExpiredStringKey(String key, String value, long expiredTime) {
        RBucket<String> bucket = redissonClient.getBucket(key);
        bucket.set(value, Duration.ofMinutes(expiredTime));
    }

    /**
     * 获取string的值
     *
     * @param key 关键
     * @return {@link T }
     */
    public String getStringKey(String key) {
        RBucket<String> bucket = redissonClient.getBucket(key);
        return bucket.get();
    }

    /**
     * 获得所有key
     *
     * @return {@link List }<{@link String }>
     */
    public List<String> getAllKey() {
        RKeys keys = redissonClient.getKeys();
        return keys.getKeysStream().collect(Collectors.toList());
    }

    /**
     * 获取List的值
     *
     * @param key 关键
     * @return {@link List }<{@link T }>
     */
    public <T> List<T> getListKey(String key) {
        RList<T> list = redissonClient.getList(key);
        return list.get();
    }

    /**
     * 设置List的值并设置过期时间
     *
     * @param key         关键
     * @param listValue   列表值
     * @param expiredTime 过期时间
     */
    public <T> void setExpiredListKey(String key, List<T> listValue, long expiredTime) {
        RList<T> list = redissonClient.getList(key);
        list.addAll(listValue);
        list.expire(Duration.ofMinutes(expiredTime));
    }

    /**
     * 设置Set的值并设置过期时间
     *
     * @param key         关键
     * @param collection  集合
     * @param expiredTime 过期时间
     */
    public <T> void setExpiredSetKey(String key, Collection<T> collection, long expiredTime) {
        RSet<T> set = redissonClient.getSet(key);
        set.addAll(collection);
        set.expire(Duration.ofMinutes(expiredTime));
    }

    /**
     * 获取Set的值
     *
     * @param key key
     * @return {@link Set }<{@link T }>
     */
    public <T> Set<T> getSetKey(String key) {
        return redissonClient.getSet(key);
    }

    /**
     * 设置Map的值并设置过期时间
     *
     * @param key         Key
     * @param collection  集合
     * @param expiredTime 过期时间
     */
    public <T, V> void setExpiredMapKey(String key, Map<T, V> collection, long expiredTime) {
        RMap<T, V> map = redissonClient.getMap(key);
        map.putAll(collection);
        map.expire(Duration.ofMinutes(expiredTime));
    }

    /**
     * 获取map的Key
     *
     * @param key key
     * @return {@link Map }<{@link T },{@link V }>
     */
    public <T, V> Map<T, V> getMapKey(String key) {
        return redissonClient.getMap(key);
    }

    /**
     * 设置Queue的值并设置过期时间
     *
     * @param key         key
     * @param collection  集合
     * @param expiredTime 过期时间
     */
    public <T> void setExpiredQueueKey(String key, Collection<T> collection, long expiredTime) {
        RQueue<T> queue = redissonClient.getQueue(key);
        queue.addAll(collection);
        queue.expire(Duration.ofMinutes(expiredTime));
    }

    /**
     * 获取List的key
     *
     * @param key 关键
     * @return {@link Queue }<{@link T }>
     */
    public <T> Queue<T> getQueueKey(String key) {
        return redissonClient.getQueue(key);
    }

    /**
     * 检查key是否存在
     *
     * @param key 关键
     * @return boolean
     */
    public boolean checkKey(String key) {
        return redissonClient.getKeys().countExists(key) > 0;
    }

    /**
     * init限流器
     *
     * @param rateKey      限流器Key值
     * @param rate         产生N个令牌
     * @param rateInterval 每秒产生令牌
     *                     创建限流器，最大流速:每10秒钟产生8个令牌
     *                     example：rateLimiter.trySetRate(RateType.OVERALL, 8, 10, RateIntervalUnit.SECONDS);
     */
    public void initRateLimiter(String rateKey, Long rate, Long rateInterval) {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(rateKey);
        rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, RateIntervalUnit.SECONDS);
    }

    /**
     * 获取令牌
     *
     * @param rateKey 限流器Key值
     * @return boolean
     */
    public boolean tryAcquire(String rateKey) {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(rateKey);
        return rateLimiter.tryAcquire();
    }

    /**
     * 尝试加锁
     *
     * @param lockKey   lock的key值
     * @param waitTime  等待获取锁的最大时间量
     * @param leaseTime 锁的自动释放时间
     * @param timeUnit  时间单位
     */
    public Boolean tryLock(String lockKey, Integer waitTime, Integer leaseTime, TimeUnit timeUnit) {
        RLock rLock = redissonClient.getLock(lockKey);
        try {
            return rLock.tryLock(waitTime, leaseTime, timeUnit);
        } catch (InterruptedException e) {
            throw ExceptionFactory.instanceServerException("redis加锁失败", e);
        }
    }

    /**
     * 解锁
     *
     * @param lockKey lock的key
     */
    public void unlock(String lockKey) {
        RLock rLock = redissonClient.getLock(lockKey);
        if (rLock.isHeldByCurrentThread()) {
            rLock.unlock();
            log.debug("释放锁完成");
        } else {
            log.debug("锁已过期......");
        }
    }
    public void distributedLockExecution(String lockKey, Integer waitTime, Integer leaseTime, TimeUnit timeUnit,Runnable function){
        try {
            if (tryLock(lockKey, waitTime, leaseTime, timeUnit)) {
                function.run();
            }else{
                throw ExceptionFactory.instanceServerException("正在处理中,请稍后检查结果或重试");
            }
        } catch (Exception e) {
            throw ExceptionFactory.instanceServerException("方法执行失败:", e);
        } finally {
            unlock(lockKey);
        }
    }
    /**
     * Init bloom过滤器
     *
     * @param bloomKey 开关键
     */
    public void initBloomFilter(String bloomKey) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomKey);
        bloomFilter.tryInit(1000000, 0.03);
    }

    /**
     * 添加bloom过滤器的值
     *
     * @param bloomKey 开关键
     * @param value    价值
     */
    public void addBloomFilter(String bloomKey, String value) {
        RBloomFilter<String> rBloomFilter = redissonClient.getBloomFilter(bloomKey);
        rBloomFilter.add(value);
    }

    /**
     * 校验布隆过滤器的值是否存在
     *
     * @param bloomKey bloom的key
     * @param value    值
     * @return boolean
     */
    public boolean checkValueBloomFilter(String bloomKey, String value) {
        RBloomFilter<String> rBloomFilter = redissonClient.getBloomFilter(bloomKey);
        return rBloomFilter.contains(value);
    }

    /**
     * 推送
     *
     * @param topicKey 主题key
     * @param message  消息
     */
    public void publish(String topicKey, String message) {
        RTopic rTopic = redissonClient.getTopic(topicKey);
        long publish = rTopic.publish(message);
        log.debug("推送接收的客户端为：{}", publish);
    }
}
